/*
* HTTPManager created by zj 
* on 2020/5/20 3:55 PM
* copyright on zhangjiang
*/

import 'dart:core';
import 'package:dio/dio.dart';
import 'package:flutter_android_tv/common_tool/zjLogFile.dart';
import 'package:flutter_android_tv/http_manager/zj_request_error.dart';


Type typeOf<T>() => T;
//设置成功回调
typedef HttpSuccessCallback<T> = void Function(dynamic data);
//失败回调
typedef HttpFailureCallback = void Function(String errorMsg);

//数据解析
typedef T JsonParse<T>(dynamic data);

/* 使用示例
HTTPManager().get(url: "search/repositories", tag: "getitems",params: {
      'page':1,
      'page_size':20,
      'q':'language:Swift',
      'sort':'stars'
    },options: Options(extra: {"refresh":false,'noCache':false}),successCallback: (res){
      print(res);
    },failureCallback: (e){
      print(e.toString());
    });

    var response = await HTTPManager().getAsync<Map<String,dynamic>>(url: "search/repositories", tag: "getitems",params: {
      'page':1,
      'page_size':20,
      'q':'language:Swift',
      'sort':'stars'
    },options: Options(extra: {"refresh":false,'noCache':false}));
    print(response);

    /// extra中可以设置context数据，在请求错误或者失败的情况下弹出错误信息

    //登录成功之后更新公共头，此后所有的请求都会带上用户信息
    dio.options.headers[HttpHeaders.authorizationHeader] = basic;
    //清空所有缓存
    Global.netCache.cache.clear();
    //更新用户的token信息
    Global.profile.token = basic;

* */

class ZJHTTPManager {
  ///设置cancelToken，可用于多个请求，当一个cancelToken取消时，所有使用该cancelToken的请求都会被取消
  ///一个页面对应一个cancelToken
  final Map<String, CancelToken> _cancelTokens = <String, CancelToken>{};

  ///设置默认的超时时间
  static const int CONNECT_TIMEOUT = 30;
  static const int RECEIVE_TIMEOUT = 30;

  late Dio _client;

  static final ZJHTTPManager _httpManager = ZJHTTPManager._internal();

  factory ZJHTTPManager() => _httpManager;

  Dio get client => _client;

  //网络请求错误的时候跳转都指定界面或者弹出提示
//  BuildContext context;
  //网络监控
  // late Connectivity _connectivity;

  ///创建实例对象
  ZJHTTPManager._internal() {
    if (_client == null) {
      BaseOptions options = BaseOptions(
        receiveTimeout: const Duration(seconds:RECEIVE_TIMEOUT),
        connectTimeout: const Duration(seconds: CONNECT_TIMEOUT),
        // headers: {"Content-Type":"application/json","content-type":"application/json"}
      );
      _client = Dio(options);
      // _connectivity = Connectivity();
    }
  }

  ///初始化公共属性
  void init(
      {String? baseUrl,
      Duration? connectTimeout,
      Duration? receiveTimeout,
      List<Interceptor>? interceptors}) {
    _client.options = _client.options.copyWith(
      baseUrl: baseUrl,
      connectTimeout: connectTimeout,
      receiveTimeout: receiveTimeout,
      headers: Map.from(_client.options.headers)
        ..remove(Headers.contentTypeHeader),
    );
    if (interceptors != null && interceptors.isNotEmpty) {
      _client.interceptors.addAll(interceptors);
    }
  }

  ///同步回调的get方法
  void get(
      {required String url,
      required String tag,
      Map<String, dynamic>? params,
      Options? options,
      HttpSuccessCallback? successCallback,
      HttpFailureCallback? failureCallback}) async {
    _request(
        url: url,
        tag: tag,
        method: 'GET',
        params: params,
        options: options,
        successCallback: successCallback,
        failureCallback: failureCallback);
  }

  ///同步回调的post方法
  void post(
      {required String url,
      required String tag,
      data,
      Map<String, dynamic>? params,
      Options? options,
      HttpSuccessCallback? successCallback,
      HttpFailureCallback? failureCallback}) async {
    _request(
        url: url,
        tag: tag,
        data: data,
        method: 'POST',
        params: params,
        options: options,
        successCallback: successCallback,
        failureCallback: failureCallback);
  }

  /// 同步上传upload方法
  void upload({
    required String url,
    required String tag,
    data,
    Map<String, dynamic>? params,
    Options? options,
    HttpSuccessCallback? successCallback,
    HttpFailureCallback? failureCallback,
    ProgressCallback? onSendProgress,
  }) async {
    _request(
        url: url,
        tag: tag,
        method: 'POST',
        data: data,
        params: params,
        options: options,
        successCallback: successCallback,
        failureCallback: failureCallback,
        onSendProgress: onSendProgress);
  }

  ///统一网络请求（同步回调的方式）
  ///
  ///[url] 网络请求地址不包含域名
  ///[tag] 请求统一标识，用于取消网络请求
  ///[method] 请求方法
  ///[data] post请求data
  ///[params] 请求参数
  ///[options] 请求配置
  ///[successCallback] 成功的回调
  ///[failureCallback] 失败的回调
  ///[progressCallback] 进度回调
  void _request({
    required String url,
    required String tag,
    String method = 'GET',
    data,
    Map<String, dynamic>? params,
    Options? options,
    HttpSuccessCallback? successCallback,
    HttpFailureCallback? failureCallback,
    ProgressCallback? onSendProgress,
  }) async {
    //检查网络是否连接
    // ConnectivityResult connectivityResult = await _connectivity.checkConnectivity();
    // if (connectivityResult == ConnectivityResult.none) {
    //   if (failureCallback != null) {
    //     failureCallback(HttpError(HttpError.NETWORK_ERROR, "网络异常，请稍后重试！"));
    //   }
    //   LogUtil.v("请求网络异常，请稍后重试！");
    //   return;
    // }
    //设置默认值

    // Map<String, dynamic> mapToke = {"content-type":"application/json"};
    // if (Global.profile.loginModel != null) {
    //   mapToke["zm_token"] = Global.profile.loginModel.token;
    // }
    options = options == null
        ? Options(
            method: method,
            receiveTimeout: Duration(seconds: onSendProgress == null ? RECEIVE_TIMEOUT : 0))
        : options.copyWith(
            receiveTimeout: Duration(seconds: onSendProgress == null ? RECEIVE_TIMEOUT : 0));

    url = _restfulUrl(url, params);
    try {
      CancelToken cancelToken = (_cancelTokens[tag] ?? CancelToken())!;
      _cancelTokens[tag] = cancelToken;

      //请求网络
      Response<Map<String, dynamic>> response = await _client.request(
        url,
        queryParameters: params,
        data: data,
        options: options,
        onSendProgress: onSendProgress,
        cancelToken: cancelToken,
      );
      if(response.data!.containsKey("flag")){
        dynamic responseCode = response.data!["flag"];
        if ((responseCode is int && responseCode == 0) ||
            (responseCode is String && responseCode == "200")) {
          if (successCallback != null) {
            successCallback(response.data!["data"]);
          }
        } else {
          if (response.statusCode == 200 && successCallback != null) {
            successCallback(response.data!["data"]);
          } else {
            String errorDesc = response.data!["errorMsg"];
            if (failureCallback != null) {
              zjLog("开始请求===失败1111");
              failureCallback(errorDesc);
            }
          }
        }
      }
    } on DioException catch (e, s) {
      zjLog("请求出错:$e\n$s");
      String code = _getErrorMsg(e);
      if (failureCallback != null && e.type != DioExceptionType.cancel) {
        failureCallback(code);
      }
    } catch (e, s) {
      zjLog("未知异常出错:$e\n$s");
      if (failureCallback != null) {
        failureCallback('网络异常，请稍后重试');
      }
    }
  }

  // ///异步Future的get方法
  // Future<T?> getAsync<T>({
  //   required String url,
  //   Map<String, dynamic>? params,
  //   data,
  //   Options? options,
  //   JsonParse<T>? jsonParse,
  // }) async {
  //   return _requestAsync(
  //       url: url,
  //       tag: url,
  //       method: "GET",
  //       params: params,
  //       data: data,
  //       options: options,
  //       jsonParse: jsonParse);
  // }

  // ///异步Future的post方法
  // Future<T?> postAsync<T>({
  //   required String url,
  //   Map<String, dynamic>? params,
  //   data,
  //   Options? options,
  //   JsonParse<T>? jsonParse,
  // }) async {
  //   return _requestAsync(
  //       url: url,
  //       tag: url,
  //       params: params,
  //       data: data,
  //       method: "POST",
  //       options: options,
  //       jsonParse: jsonParse);
  // }

  // ///异步Future的upload方法
  // Future<T?> uploadAsync<T>({
  //   required String url,
  //   required String tag,
  //   data,
  //   ProgressCallback? onSendProgress,
  //   Map<String, dynamic>? params,
  //   Options? options,
  //   JsonParse<T>? jsonParse,
  // }) async {
  //   return _requestAsync(
  //       url: url,
  //       tag: tag,
  //       data: data,
  //       method: "POST",
  //       params: params,
  //       options: options,
  //       onSendProgress: onSendProgress,
  //       jsonParse: jsonParse);
  // }

  ///异步Future的方法
  ///
  ///[url] 网络请求地址不包含域名
  ///[tag] 请求统一标识，用于取消网络请求
  ///[method] 请求方法
  ///[data] post请求data
  ///[params] 请求参数
  ///[options] 请求配置
  ///[jsonParse] json解析
  // Future<T?> _requestAsync<T>({
  //   required String url,
  //   required String tag,
  //   String method = 'GET',
  //   Map<String, dynamic>? params,
  //   data,
  //   Options? options,
  //   ProgressCallback? onSendProgress,
  //   JsonParse<T>? jsonParse,
  // }) async {
  //   //检查网络是否连接
  //   // ConnectivityResult connectivityResult = await _connectivity.checkConnectivity();
  //   // if (connectivityResult == ConnectivityResult.none) {
  //   //   LogUtil.v("请求网络异常，请稍后重试！");
  //   //   throw "网络异常，请稍后重试！";
  //   // }
  //   params = params ?? {};
  //   // Map<String, dynamic> mapToke = {"content-type":"application/json"};
  //   // if (Global.profile.loginModel != null) {
  //   //   mapToke["zm_token"] = Global.profile.loginModel.token;
  //   // }
  //   options = options == null
  //       ? Options(
  //       method: method,
  //       receiveTimeout: Duration(seconds: onSendProgress == null ? RECEIVE_TIMEOUT : 0))
  //       : options.copyWith(
  //       receiveTimeout: Duration(seconds: onSendProgress == null ? RECEIVE_TIMEOUT : 0));
  //
  //   url = _restfulUrl(url, params);
  //   zjLog("当前的泛型类型为: ${typeOf<T>()}");
  //   Response response;
  //   try {
  //     CancelToken cancelToken = (_cancelTokens[tag] ?? CancelToken())!;
  //     _cancelTokens[tag] = cancelToken;
  //
  //     response = await _client.request(
  //       url,
  //       queryParameters: params,
  //       options: options,
  //       cancelToken: cancelToken,
  //       data: data,
  //       onSendProgress: onSendProgress,
  //     );
  //     if (jsonParse != null) {
  //       return jsonParse(response.data);
  //     } else {
  //       var tempData = response.data;
  //       //对于企业端设置,获取对应的data
  //       if (tempData is Map) {
  //         if (tempData.keys.contains("code")) {
  //           int responseCode = tempData["code"];
  //           if (responseCode == 200 || responseCode == 0) {
  //             //成功状态
  //             dynamic data = tempData["data"];
  //             zjLog("当前是Map===${typeOf<T>()}");
  //             if (data == null){
  //               zjLog("data 当前是null");
  //               return null;
  //             }
  //             if (typeOf<T>().toString() == 'Map<dynamic, dynamic>') {
  //               return data as T;
  //             } else if (typeOf<T>().toString() == 'Map<String, dynamic>') {
  //               return data as T;
  //             }
  //
  //             if (data is Map && data.runtimeType.toString() == 'Map<String, dynamic>') {
  //               T templateModel = JsonConvert.fromJson<T>(data as Map<String,dynamic>) as T;
  //               if (templateModel != null) {
  //                 return templateModel;
  //               }
  //             } else if (data is List) {
  //               T tem = JsonConvert.fromJsonAsT<T>(data);
  //               return tem;
  //             } else if ((typeOf<T>() is int)) {
  //               zjLog("当前返回的数据===${response.statusCode}");
  //               return response.statusCode as T;
  //             }
  //           } else {
  //             //获取错误error
  //             String errorStr = tempData["message"];
  //             zjLog("请求出错:$errorStr\n");
  //             //抛出异常
  //             throw errorStr;
  //           }
  //         } else if (tempData.keys.contains("flag")) {
  //           dynamic responseCode = tempData["flag"];
  //           if ((responseCode is int && responseCode == 0) ||
  //               (responseCode is String && responseCode == "200")) {
  //             dynamic data = tempData["data"];
  //             zjLog("当前是Map===${typeOf<T>()}");
  //             if (typeOf<T>().toString() == 'Map<dynamic, dynamic>') {
  //               return data as T;
  //             } else if (typeOf<T>().toString() == 'Map<String, dynamic>') {
  //               return data as T;
  //             }
  //             if (data is Map && data.runtimeType.toString() == 'Map<String, dynamic>') {
  //               T templateModel = JsonConvert.fromJson<T>(data as Map<String,dynamic>) as T;
  //               if (templateModel != null) {
  //                 return templateModel;
  //               }
  //             } else if (data is List) {
  //               T tem = JsonConvert.fromJsonAsT<T>(data);
  //               return tem;
  //             } else if ((typeOf<T>() is int)) {
  //               zjLog("当前返回的数据===${response.statusCode}");
  //               return response.statusCode as T;
  //             }
  //           } else {
  //             //直接取错误信息
  //             String errorMsg = tempData["errorMsg"];
  //             throw errorMsg;
  //           }
  //         }
  //       }
  //     }
  //   } on DioError catch (e, s) {
  //     String code = _getErrorMsg(e);
  //     throw code;
  //   } catch (e, s) {
  //     zjLog("未知异常错误:$e\n$s");
  //     throw "$e";
  //   }
  // }

  ///同步回调下载文件方法
  void download({
    required String url,
    required String savePath,
    required String tag,
    ProgressCallback? onReceiveProgress,
    Map<String, dynamic>? params,
    data,
    Options? options,
    HttpSuccessCallback? successCallback,
    HttpFailureCallback? failureCallback,
  }) async {
    // ConnectivityResult result = await Connectivity().checkConnectivity();
    // if(result == ConnectivityResult.none){
    // LogUtil.v("请求网络异常，请稍后重试");
    // throw (HttpError(HttpError.NETWORK_ERROR,"网络异常，请稍后再试"));
    // }
    params = params ?? {};
    ////0代表不设置超时
    int receiveTimeout = 0;
    options ??= options == null
        ? Options(receiveTimeout: Duration(seconds: receiveTimeout))
        : options.copyWith(receiveTimeout: Duration(seconds: receiveTimeout));

    url = _restfulUrl(url, params);

    try {
      CancelToken cancelToken = _cancelTokens[tag] ?? CancelToken();
      _cancelTokens[tag] = cancelToken;

      Response response = await _client.download(url, savePath,
          queryParameters: params,
          options: options,
          data: data,
          onReceiveProgress: onReceiveProgress,
          cancelToken: cancelToken);
      if (successCallback != null) {
        successCallback(response.data);
      }
    } on DioException catch (e, s) {
      zjLog("请求出错:$e\n$s");
      String code = _getErrorMsg(e);
      if (failureCallback != null) {
        failureCallback(code);
      }
    } catch (e, s) {
      zjLog("未知异常错误:$e\n$s");
      if (failureCallback != null) {
        failureCallback("网络异常，请稍后重试");
      }
    }
  }

  ///同步回调下载文件方法
  Future<Response> downloadAsync({
    required String url,
    required String savePath,
    required String tag,
    ProgressCallback? onReceiveProgress,
    Map<String, dynamic>? params,
    data,
    Options? options,
  }) async {
    // ConnectivityResult result = await Connectivity().checkConnectivity();
    // if (result == ConnectivityResult.none) {
    //   LogUtil.v("请求网络异常，请稍后重试");
    //   throw (HttpError(HttpError.NETWORK_ERROR, "网络异常，请稍后再试"));
    // }
    params = params ?? {};
    ////0代表不设置超时
    int receiveTimeout = 0;
    options ??= options == null
        ? Options(receiveTimeout: Duration(seconds: receiveTimeout))
        : options.copyWith(receiveTimeout: Duration(seconds: receiveTimeout));
    url = _restfulUrl(url, params);
    try {
      CancelToken cancelToken = _cancelTokens[tag] ?? CancelToken();
      _cancelTokens[tag] = cancelToken;

      return _client.download(url, savePath,
          queryParameters: params,
          options: options,
          data: data,
          onReceiveProgress: onReceiveProgress,
          cancelToken: cancelToken);
    } on DioException catch (e, s) {
      zjLog("请求出错:$e\n$s");
      throw ZJHttpError.dioError(e);
    } catch (e, s) {
      zjLog("未知异常错误:$e\n$s");
      throw ZJHttpError(ZJHttpError.UNKNOWN, "网络异常，请稍后重试");
    }
  }

  ///取消网络请求，请求原因可选
  void cancel(String tag, [dynamic reasonStr]) {
    if (_cancelTokens.containsKey(tag)) {
      if (!_cancelTokens[tag]!.isCancelled) {
        _cancelTokens[tag]!.cancel(reasonStr);
      }
      _cancelTokens.remove(tag);
    }
  }

  /// restful处理
  String _restfulUrl(String url, Map<String, dynamic>? params) {
    //restful请求处理
    if(params == null) {
      return url;
    }
    params.forEach((key, value) {
      if (url.contains(key)) {
        url = url.replaceAll(":$key", value.toString());
      }
    });
    return url;
  }

  String _getErrorMsg(DioException e) {
    String code = "";
    switch (e.type) {
      case DioExceptionType.cancel:
        code = "取消连接请求";
        break;
      case DioExceptionType.connectionTimeout:
        code = "连接超时，请重试";
        break;
      case DioExceptionType.receiveTimeout:
        code = "接收超时，请重试";
        break;
      case DioExceptionType.sendTimeout:
        code = "发送请求超时，请重试";
        break;
      case DioExceptionType.badResponse:
        code = "服务君出差了，请重试";
        if (e.response!.statusCode == 403) {
          //没有权限，请重新登录
          // eventBus.fire(LoginFailEvent(loginToken: code));
        }
        break;
      default:
        code = "连接失败，请重试";

    }
    return code;
  }
}
